package KAUFLICK;

import java.awt.*;
import java.awt.geom.AffineTransform;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.util.*;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
import javax.swing.JPanel;

public class BallManager {
	final double IMPULSE_TIME = 0.02;		// flicking impulse period (unit: sec)
	final double PIXEL_METER_RATIO = 1;		// pixel-meter scaler (1 pixel = 1 meter = 1,000,000 mm)
	final double FORCE_SCALER = 100;		// length-force scaler
	
	private JPanel box; 
	private ArrayList<Ball> BallList;
	private collisionAssistant collisionAssist;
	private PipedOutputStream pout;
	private PipedInputStream pin;
	
	public BallManager(JPanel b) {
		BallList = new ArrayList<Ball>();
		box = b;
		try {
			pout = new PipedOutputStream();
			pin = new PipedInputStream(pout);
			
			/* activate collision detector-solver : communicate with ball by pipe */
			collisionAssist = new collisionAssistant(pin);	
			collisionAssist.setDaemon(true);
			collisionAssist.start();
			
			/* play sound */
		    Sound s = new Sound("c:/MEDIA/imperial_march.wav");
			s.start();
		}
		catch (IOException e) { System.out.println(e); }
	}
	
	public void newBall() {
		Ball b = new Ball(box, pout);
		BallList.add(b);
	}
	public void newBall(Image img, String fireSND, String CollisionSND, int player_num, String name, double size, double x, double y, double weight) {
		Ball b = new Ball(box, img, fireSND, CollisionSND, player_num, name, size, x, y, weight, pout);
		BallList.add(b);
	}
	public int getTotalAliveBallCount() {
		return BallList.size();
	}
	public void draw() {
		for (Ball b : BallList)
			b.draw();
	}
	public void draw(Graphics g) {
		for (Ball b : BallList)
			b.draw(g);
	}
	public void flick(Point start, Point end) {
		System.out.println("flick");
		double length_h;	// width (unit: pixel)
		double length_v;	// height (unit: pixel)
		double length_s;	// slope (unit: pixel)
		double dx, dy;		// directional vector component
		double a;			// acceleration (unit: m/s^2)
		double r, r_x, r_y;	// length of (ball center point) and (striking point) (unit: pixel)
		double torque;		// torque (unit: kg*m);
		double rpm;			// rpm (unit: rpm);
		double theta;
		

		/* check the all balls' action completed */
		if (isAllStopped() == false)
			return ;
		
		/* remove expired balls */
		checkFalledBall();
		
		/* if list has empty, do nothing */
		if (BallList.isEmpty())
			return ;
		
		/* if start == end point, do nothing */
		if (start.x == end.x && start.y == end.y) 
			return ;
		
		/* find selected ball */
		Ball b = getTargetBall(start);
		if (b == null)	// if nothing has been selected, cancel the processing 
			return ;	
		
		
		/* calculate various factors */
		length_h = start.x - end.x;	// vector direction, reversed
		length_v = start.y - end.y;	// vector direction, reversed
		length_s = Math.sqrt((Math.pow(length_h, 2) + Math.pow(length_v, 2))); // determine magnitude
		
		dx = length_h / length_s;	// determine unit vector
		dy = length_v / length_s;
		
		a = (length_s * FORCE_SCALER) / b.getMass(); 	// calculate acceleration from force, f=m*a, a = f/m 
		
		r_x = start.x - (b.getX() + (b.getSize()/2));	// calculate torque and revolution
		r_y = start.y - (b.getY() + (b.getSize()/2));
		r = Math.sqrt(Math.pow(r_x, 2) + Math.pow(r_y, 2));
		if (length_h < 0)								
			theta = Math.atan(length_v/length_h) + Math.PI;
		else
			theta = Math.atan(length_v/length_h);
		if (r_x < 0)
			theta -= Math.atan(r_y/r_x) + Math.PI;
		else
			theta -= Math.atan(r_y/r_x);
		torque = r * length_s * Math.sin(theta);
		rpm = torque / b.getMass();	
		
		/* set various factors */
		b.setDx(dx);
		b.setDy(dy);
		
		b.setVelocity(a * IMPULSE_TIME);	// v = a*t;
		b.setRPM(rpm);
		
		/* ball run */
		if (b.isAlive() == false) {		// first start
			b.setDaemon(true);
			b.start(); 
		}
		else							// after first start
			b.reStart();		
		
		/* play sound */
		if (b.getFireSoundFile() != null) {
			Sound s = new Sound(b.getFireSoundFile());
			s.start();
		}	
	}
	private Ball getTargetBall(Point p) {
		for (Ball b : BallList) {
			if ( (b.getX() <= p.x) && (p.x <= b.getX() + b.getSize()) )
				if ( (b.getY() <= p.y) && (p.y <= b.getY() + b.getSize()) )
					return b;
		}
		return null;
	}
	private void checkFalledBall() {	
		Ball b;
		for (int i = 0; i < BallList.size(); i++) {
			b = BallList.get(i);
			if (b.IsBallAlive() == false) {
				BallList.remove(b);
				//b.interrupt();			// terminate thread	@ DO NOT TERMINATE THREAD. BECAUSE OF PIPE
				if (BallList.isEmpty())	// if it was last item, exit the loop
					break;
				i--;					// re-check this indexed item
			}
		}
	}
	private boolean isAllStopped() {
		for (Ball b : BallList) {
			if (b.IsBallMoving())
				return false;
		}
		return true;
	}
	
	private class collisionAssistant extends Thread {
		private DataInputStream in;
		
		collisionAssistant(InputStream is) {
			in = new DataInputStream(is);
		}
		public void run() {
			synchronized (this) {
				int n;
				Ball cb, b;
				while (true) {
					try {						
						/* get message and find ball which sent message */
						n = in.readInt();	// get message
						System.out.println("collisionAssistant: " + n);
						if (BallList.isEmpty() == false)	{ 	// check ArrayList to avoid null pointer exception
																// if BallList is empty, discard the message. no 'while(true) wait();'
							cb = null;
							for (Ball tb : BallList) { 	// find ball
								if (tb.getID() == n) {
									cb = tb;	// set criteria ball
									break;
								}
							}	
							/* check collision */ 
							if (cb != null) {
								for (int j = 0; j < BallList.size(); j++) {
									b = BallList.get(j);
									if (cb.getID() == b.getID())	// if same object, skip.
										continue;
									if (isCollided(cb, b)) 
										collisionSolver(cb, b);
								}
							}
						}
					} 
					catch (EOFException e) { System.out.println(e); }
					catch (IOException e ) { System.out.println(e); }
				}
			}
		}
		private boolean isCollided(Ball a, Ball b) {
			System.out.println("isCollided: " + a.getID() + " " + b.getID());
			/* follow variables was used for readability.. */
			double a_r, b_r;		// radius
			double a_cp_x, b_cp_x;	// center point x
			double a_cp_y, b_cp_y; 	// center point y
			double distance_AB;		// scalar of vector a-b
			
			a_r = a.getSize() / 2;	// get radius
			b_r = b.getSize() / 2;
			
			a_cp_x = a.getX() + a_r;	// get center point
			b_cp_x = b.getX() + b_r;
			a_cp_y = a.getY() + a_r;
			b_cp_y = b.getY() + b_r;
			
			distance_AB = Math.sqrt( Math.pow(a_cp_x - b_cp_x, 2) + Math.pow(a_cp_y - b_cp_y, 2) );
			if (distance_AB < (a_r + b_r) )
				return true;	// it was collided.
			else
				return false;	// it wasn't collided.
		}
		private void collisionSolver(Ball a, Ball b) {
			//System.out.println("collisionSolver start: " + a.getID() + " " + b.getID());	/* for debug */
			/* get velocity */
			double a_v = a.getVelocity();
			double b_v = b.getVelocity();
			
			if ( (a_v <= 0) && (b_v <= 0) ) {		// if both balls stopped, but still collided - adjust position
				//System.out.println("collisionSolver adjust: " + a.getID() + " " + b.getID());	/* for debug */
				/* get center point */
				double a_cp_x = a.getX() + a.getSize() / 2;	
				double b_cp_x = b.getX() + b.getSize() / 2;
				double a_cp_y = a.getY() + a.getSize() / 2;
				double b_cp_y = b.getY() + b.getSize() / 2;
				
				/* calculate the length of gaps */
				double adjust_len = ((a.getSize() + b.getSize()) / 2) - Math.sqrt( Math.pow(a_cp_x-b_cp_x,2) + Math.pow(a_cp_y-b_cp_y,2) );
				adjust_len += 2;	// margin 
				
				/* norm vector of a-b collision line */
				double angle_AB_norm = Math.atan( (b_cp_y - a_cp_y) / (b_cp_x - a_cp_x) );	
				if ( (b_cp_x - a_cp_x) < 0)
					angle_AB_norm += Math.PI;
				
				/* get adjust factor */
				double adjust_x = adjust_len * Math.cos(angle_AB_norm) / 2;
				double adjust_y = adjust_len * Math.sin(angle_AB_norm) / 2;
				
				/* adjusting */
				if (a.getX() < b.getX()) {	
					a.setX(a.getX() - adjust_x);
					b.setX(b.getX() + adjust_x);
				}
				else {
					a.setX(a.getX() + adjust_x);
					b.setX(b.getX() - adjust_x);
				}
				if (a.getY() < b.getY()) {
					a.setY(a.getY() - adjust_y);
					b.setY(b.getY() + adjust_y);
				}
				else {
					a.setY(a.getY() + adjust_y);
					b.setY(b.getY() - adjust_y);
				}
				a.checkMeCollision();
				b.checkMeCollision();
			}	
			
			if (a_v <= 0)
				return;		// already stopped
			if (b_v <= 0)
				b_v = 0;
			
			/* get momentum */ 
			double p_a = a_v * a.getMass();
			double p_b = b_v * b.getMass();
			
			/* get center point */
			double a_cp_x = a.getX() + a.getSize() / 2;	
			double b_cp_x = b.getX() + b.getSize() / 2;
			double a_cp_y = a.getY() + a.getSize() / 2;
			double b_cp_y = b.getY() + b.getSize() / 2;
			
			/* calculate with a momentum */
			//System.out.println("collisionSolver calc a: " + a.getID() + " " + b.getID());		/* for debug */
			double angle_AB_norm = Math.atan( (b_cp_y - a_cp_y) / (b_cp_x - a_cp_x) );	// norm vector of a-b collision line 
			if ( (b_cp_x - a_cp_x) < 0)
				angle_AB_norm += Math.PI;
			double angle_AB_tan = angle_AB_norm - (Math.PI / 2);	// tangent vector of a-b collision line
			
			double angle_a_direction = (a.getDx() < 0)? (Math.atan(a.getDy() / a.getDx()) + Math.PI) : Math.atan(a.getDy() / a.getDx());	// get angle of ball's direction 
			double angle_norm_and_a_direction = angle_AB_norm - angle_a_direction;	// the included angle of norm vector and direction vector
			
			double temp_p_a_x = p_a * Math.sin(angle_norm_and_a_direction) * Math.cos(angle_AB_tan);	// the momentum of a after collision (x-axis)
			double temp_p_a_y = p_a * Math.sin(angle_norm_and_a_direction) * Math.sin(angle_AB_tan);	// the momentum of a after collision (y-axis)
			double temp_p_b_x = p_a * Math.cos(angle_norm_and_a_direction) * Math.cos(angle_AB_norm);	// the momentum of b after collision (x-axis)
			double temp_p_b_y = p_a * Math.cos(angle_norm_and_a_direction) * Math.sin(angle_AB_norm);	// the momentum of b after collision (y-axis)
			
			
			/* calculate with b momentum */
			//System.out.println("collisionSolver calc b: " + a.getID() + " " + b.getID());		/* for debug */
			angle_AB_norm *= (-1);
			angle_AB_tan *= (-1);
			double angle_b_direction = (b.getDx() < 0)? (Math.atan(b.getDy() / b.getDx()) + Math.PI) : Math.atan(b.getDy() / b.getDx());
			double angle_norm_and_b_direction = angle_AB_norm - angle_b_direction;
			
			temp_p_a_x += p_b * Math.sin(angle_norm_and_b_direction) * Math.cos(angle_AB_tan);	// the momentum of a after collision (x-axis)
			temp_p_a_y += p_b * Math.sin(angle_norm_and_b_direction) * Math.sin(angle_AB_tan);	// the momentum of a after collision (y-axis)
			temp_p_b_x += p_b * Math.cos(angle_norm_and_b_direction) * Math.cos(angle_AB_norm);	// the momentum of b after collision (x-axis)
			temp_p_b_y += p_b * Math.cos(angle_norm_and_b_direction) * Math.sin(angle_AB_norm);	// the momentum of b after collision (y-axis)
			
			
			/* get final momentum after collision */
			p_a = Math.sqrt( Math.pow(temp_p_a_x, 2) + Math.pow(temp_p_a_y, 2));	
			p_b = Math.sqrt( Math.pow(temp_p_b_x, 2) + Math.pow(temp_p_b_y, 2));		
			
			/* calculate torque and revolution about b */
			double r = b.getSize() / 2;
			double torque = r * p_b * Math.sin(angle_norm_and_a_direction);
			double rpm = torque / b.getMass();	
			
			/* write the result value */
			//System.out.println("collisionSolver setting: " + a.getID() + " " + b.getID());	/* for debug */
			a.setVelocity(p_a / a.getMass());
			b.setVelocity(p_b / b.getMass());
			//System.out.println("collisionSolver setting: " + p_a / a.getMass() + " " + p_b / b.getMass());	/* for debug */
			
			a.setDx(temp_p_a_x / p_a);
			a.setDy(temp_p_a_y / p_a);
			b.setDx(temp_p_b_x / p_b);
			b.setDy(temp_p_b_y / p_b);
			//System.out.println("collisionSolver setted: " + p_a / a.getMass() + " " + p_b / b.getMass());		/* for debug */
			
			b.setRPM(rpm);
			
			/* ball run */
			if (b.isAlive() == false) {		// first start
				b.setDaemon(true);
				b.start(); 
			}
			else if (b.IsBallMoving() == false)		// after first start
				b.reStart();
			//System.out.println("collisionSolver ball b run: " + a.getID() + " " + b.getID());	/* for debug */
			
			/* play sound */
			if (a.getCollisionSoundFile() != null) {
				Sound s = new Sound(a.getCollisionSoundFile());
				s.start();
			}
			if (b.getCollisionSoundFile() != null) {
				Sound s = new Sound(b.getCollisionSoundFile());
				s.start();
			}
		}
	}
}


///
/// have to do : 1. image rotation		- completed
///              2. collision			- completed
///				 3. flick sound			- completed
///              4. torque				- completed
///
class Ball extends Thread {
	final double MAX_BALL_SIZE = 100;
	final double MIN_BALL_SIZE = 20;
	final double DEFAULT_BALL_SIZE = 50;	
	
	final double MAX_POS_X = 800;
	final double MAX_POS_Y = 660;
	final double MIN_POS_X = 0;
	final double MIN_POS_Y = 0;
	final double DEFAULT_POS_X = 100;
	final double DEFAULT_POS_Y = 100;
	
	final double MAX_MASS = 200;
	final double MIN_MASS = 20;
	final double DEFAULT_MASS = 60;
	
	final int DEFAULT_PLAYER_N = 1;
	
	final double PIXEL_METER_RATIO = 1;	// pixel-meter scaler (1 pixel = 1 meter)
	final double UPDATE_PERIOD = 0.02;	// screen update period (0.02 sec = 50 frame/s)
	
	final double IMPULSE_TIME = UPDATE_PERIOD;
	final double FRICTION_FACTOR = 0.5;
	final double GRAVIT_ACCEL = 9.81;		// Gravitational acceleration (unit: m/(s^2) )
	
	private JPanel box;
	private Image img;
	private String soundFilef;	// fire sound
	private String soundFilec;	// Collision sound 
	private boolean isMoving;	// is this object moving?
	private int playerID;		// belonged team number
	
	private String name;
	private boolean isAlive;	// true: alive, false: defeated
	static private int n = 0;	// number of total instances
	private int ID;
	private double size;		// ball size 		(unit: meter)
	private double pos_x;		// ball position	(unit: meter, coordinate)
	private double pos_y;		// ball position	(unit: meter, coordinate)
	private double mass;		// ball mass		(unit: kg)
	private double velocity;	// ball speed		(unit: m/s)
	private double heading_dx;	// ball direction	(unit: meter)
	private double heading_dy;	// ball direction	(unit: meter)
	private double rpm;			// ball spin speed	(unit: revolution per minute)
	private double torque;		// ball spin force	(unit: N*m)
	private double angle;		// compensation angle	(unit: radian)
	
	private DataOutputStream out;	// thread pipe - message out (send isCollided? message - like interrupt flag. this message will be queued at msgRouter)
	
	/* constructor */
	Ball(JPanel box, OutputStream out) {
		this.box 		= box;
		this.img	 	= null;
		this.soundFilef	= null;
		this.soundFilec = null;
		this.playerID 	= DEFAULT_PLAYER_N;
		this.isMoving 	= false;
		
		this.name 		= "";
		this.isAlive 	= true;
		this.ID 		= ++n;
		this.size 		= DEFAULT_BALL_SIZE;
		this.pos_x 		= DEFAULT_POS_X;
		this.pos_y 		= DEFAULT_POS_Y;
		this.mass 		= DEFAULT_MASS;
		this.velocity 	= 0;
		this.heading_dx	= 0;
		this.heading_dy	= -1;
		this.rpm 		= 0;
		this.torque 	= 0;
		this.angle		= 0;
		
		this.out 		= new DataOutputStream(out);
	}
	Ball(JPanel box, Image img, String soundFire, String soundCollision, int playerID, String name, double size, double pos_x, double pos_y, double mass, OutputStream out) {
		this.box 		= box;
		this.img 		= img;
		this.soundFilef	= soundFire;
		this.soundFilec = soundCollision;
		this.playerID 	= playerID;
		this.isMoving 	= false;
		
		this.name 		= name;
		this.isAlive 	= true;
		this.ID 		= ++n;
		if (size < MIN_BALL_SIZE || size > MAX_BALL_SIZE)
			this.size = DEFAULT_BALL_SIZE;
		else
			this.size = size;
		if (pos_x < MIN_POS_X || pos_x > MAX_POS_X)
			this.pos_x = DEFAULT_POS_X;
		else
			this.pos_x = pos_x;
		if (pos_y < MIN_POS_Y || pos_y > MAX_POS_Y)
			this.pos_y = DEFAULT_POS_Y;
		else
			this.pos_y = pos_y;
		if (mass < MIN_MASS || mass > MAX_MASS)
			this.mass = DEFAULT_MASS;
		else
			this.mass 	= mass;
		
		this.velocity 	= 0;
		this.heading_dx	= 0;
		this.heading_dy	= -1;
		this.rpm 		= 0;
		this.torque 	= 0;
		this.angle		= 0;
		
		this.out 		= new DataOutputStream(out);
	}
	
	/* method */
	public void moveTo(double x, double y) {
		pos_x = x;
		pos_y = y;
		box.repaint();
	}
	public double getBallAngle() {
		if (heading_dx < 0)
			return (Math.atan(heading_dy/heading_dx) - (Math.PI / 2)) + angle;
		else
			return (Math.atan(heading_dy/heading_dx) + (Math.PI / 2)) + angle;
	}
	public void checkMeCollision() {
		try {
			System.out.println("pipe out : " + ID);
			out.writeInt(ID);
			out.flush();
		}
		catch (IOException e) { System.out.println(e); }
	}
	public void draw() {
		Graphics g = box.getGraphics();
		this.draw(g);
		g.dispose();
	}
	public void draw(Graphics g) {
		if (isAlive == false)
			return ;
		if (playerID == 1)
			g.setColor(Color.black);
		else
			g.setColor(Color.white);
		g.drawOval((int)pos_x, (int)pos_y, (int) size, (int)size);
		g.fillOval((int)pos_x, (int)pos_y, (int) size, (int)size);
		
		AffineTransform affineTransform = new AffineTransform(); 
		affineTransform.translate((pos_x+size/2) +(Math.sqrt(2)/2)*size*Math.sin(getBallAngle()-Math.PI/4), (pos_y+size/2) +(Math.sqrt(2)/2)*size*Math.sin(getBallAngle()-Math.PI*3/4));
		affineTransform.rotate( getBallAngle() ); 
		Graphics2D g2d = (Graphics2D) g;
		g2d.drawImage(img, affineTransform, box); 
	}
	public void run() {
		try {
			synchronized (this) {
				while (true) {
					isMoving = true;
					if (velocity <= 0) {
						isMoving = false;
						rpm = 0;
						wait(); 
					}
					else {
						pos_x = pos_x + (heading_dx * velocity);
						pos_y = pos_y + (heading_dy * velocity);
						if (isFalled()) {
							isAlive = false;
							velocity = 0;
							Sound s = new Sound("c:/media/warp.wav");
							s.start();
						}
						box.repaint();
						velocity -= (getFriction() / mass) * IMPULSE_TIME;
						rpm *= 0.98; 
						angle += 2*Math.PI * (rpm / 60) * IMPULSE_TIME;
						if (angle >= 2*Math.PI)
							angle -= 2*Math.PI;
						System.out.println("checkMeCollision " + ID);
						checkMeCollision();		// send follow message through pipe-out : "I'm moving now. Is I collided with other ball? Check me."
						sleep((int)(IMPULSE_TIME * 1000));	// refresh time 
					}
				}
			}
		}
		catch (InterruptedException e) { }
		
		//try {
			//out.close();
		//} catch (IOException e) { }
	}
	public synchronized void reStart() {
		notify();
	}
	private double getFriction() {
		return FRICTION_FACTOR * mass * GRAVIT_ACCEL; 
	}
	private boolean isFalled() {
		if ((pos_x+size/2) < MIN_POS_X || (pos_x+size/2) > MAX_POS_X || (pos_y+size/2) < MIN_POS_Y || (pos_y+size/2) > MAX_POS_Y )
			return true;
		else
			return false;
	}
	
	/* Accessors */
	public int getOwner() { return playerID; }
	public boolean IsBallMoving() { return isMoving; }
	public boolean IsBallAlive() { return isAlive; }
	public String getBallName() { return name; }
	public String getFireSoundFile() { return soundFilef; }
	public String getCollisionSoundFile() { return soundFilec; }
	public int getN() { return n; }
	public int getID() { return ID; }
	public double getSize() { return size; }
	public double getX() { return pos_x; }
	public double getY() { return pos_y; }
	public double getMass() { return mass; }
	public double getVelocity() { return velocity; }
	public double getDx() { return heading_dx; }
	public double getDy() { return heading_dy; }
	public double getRPM() { return rpm; }
	public double getTorque() { return torque; }
	
	/* Mutator */
	//public String setName(String n) { name = n; }	// immutable
	//public void setID(int id) { ID = id; } 		// immutable
	//public void setSize(int s) { size = s; }		// immutable
	public void setX(double x) { pos_x = x; box.repaint(); }
	public void setY(double y) { pos_y = y; box.repaint(); }
	//public void setWeight(int w) { weight = w; }	// immutable
	public void setVelocity(double v) { velocity = v; }
	public void setDx(double dx) { heading_dx = dx; }
	public void setDy(double dy) { heading_dy = dy; }
	public void setRPM(double r) { rpm = r; }
	public void setTorque(double t) { torque = t; }
	
}

class Sound extends Thread {
	private final int BUFFER_SIZE = 128000; 
    private File soundFile; 
    private AudioInputStream audioStream; 
    private AudioFormat audioFormat; 
    private SourceDataLine sourceLine; 
    private String strFilename;
    
    Sound(String filename) {
    	this.strFilename = filename;
    }
    public void playSound(){ 
 
        try { 
            soundFile = new File(strFilename); 
        } catch (Exception e) { 
            e.printStackTrace(); 
            System.exit(1); 
        } 
 
        try { 
            audioStream = AudioSystem.getAudioInputStream(soundFile); 
        } catch (Exception e){ 
            e.printStackTrace(); 
           System.exit(1); 
        } 
 
        audioFormat = audioStream.getFormat(); 
 
        DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat); 
        try { 
            sourceLine = (SourceDataLine) AudioSystem.getLine(info); 
            sourceLine.open(audioFormat); 
        } catch (LineUnavailableException e) { 
            e.printStackTrace(); 
            System.exit(1); 
        } catch (Exception e) { 
            e.printStackTrace(); 
            System.exit(1); 
        } 
 
        sourceLine.start(); 
 
        int nBytesRead = 0; 
        byte[] abData = new byte[BUFFER_SIZE]; 
        while (nBytesRead != -1) { 
            try { 
                nBytesRead = audioStream.read(abData, 0, abData.length); 
            } catch (IOException e) { 
                e.printStackTrace(); 
            } 
            if (nBytesRead >= 0) { 
                @SuppressWarnings("unused") 
                int nBytesWritten = sourceLine.write(abData, 0, nBytesRead); 
            } 
        } 
 
        sourceLine.drain(); 
        sourceLine.close(); 
    } 
    public void run() {
    	playSound();
    }

}
